using System.Globalization;
using Engine;
using Game;
using GameEntitySystem;
using TemplatesDatabase;
using Random = Game.Random;

namespace SCIENEW {
    public abstract class ComponentTankBase : Component, ITank {
        
        public class Tank {
            public int Value;
            public float Volume;
            public float Capacity = 4f;
        }

        public List<Tank> m_tanks = [];

        Project ITank.Project => Project;

        public Random m_random = new();

        public virtual int TanksCount => m_tanks.Count;

        public int VisibleTanksCount {
            get => TanksCount;
            set { }
        }

        /// <summary>
        /// 为指定流体寻找合适的流体槽
        /// </summary>
        /// <param name="inventory"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static int FindAcquireTankForFluid(ITank tank, int value) {
            for (int i = 0; i < tank.TanksCount; i++) {
                if (tank.GetTankVolume(i) > 0
                    && tank.GetTankValue(i) == value
                    && tank.GetTankVolume(i) < tank.GetTankCapacity(i, value)) {
                    return i;//某个槽放了这个液体，但是没放满，还可以继续放
                }
            }
            for (int j = 0; j < tank.TanksCount; j++) {
                if (tank.GetTankVolume(j) == 0
                    && tank.GetTankCapacity(j, value) > 0) {
                    return j;//某个槽是空的，还允许放液体
                }
            }
            return -1;
        }

        /// <summary>
        /// 让一个容器装入液体，不管格子是哪个
        /// </summary>
        /// <param name="tank"></param>
        /// <param name="value"></param>
        /// <param name="volume"></param>
        /// <returns>实在装不下的液体体积</returns>
        public static float AcquireFluid(ITank tank, int value, float volume) {
            while (volume > 0) {
                int acquirableTank = FindAcquireTankForFluid(tank, value);
                if (acquirableTank < 0) {
                    break;
                }
                tank.AddTankFluid(acquirableTank, value, volume, out float overflow);
                volume = overflow;
            }
            return volume;
        }

        public ComponentPlayer FindInteractingPlayer() {
            ComponentPlayer componentPlayer = Entity.FindComponent<ComponentPlayer>();
            if (componentPlayer == null) {
                ComponentBlockEntity componentBlockEntity = Entity.FindComponent<ComponentBlockEntity>();
                if (componentBlockEntity != null) {
                    var position = new Vector3(componentBlockEntity.Coordinates);
                    componentPlayer = Project.FindSubsystem<SubsystemPlayers>(throwOnError: true).FindNearestPlayer(position);
                }
            }
            return componentPlayer;
        }

        public static void EmitTankFluid(ITank tank, int tankIndex, Vector3 position, Vector3 velocity) {
            float tankVolume = tank.GetTankVolume(tankIndex);
            if (tankVolume > 0) {
                int tankValue = tank.GetTankValue(tankIndex);
                float num = tank.RemoveTankFluid(tankIndex, tankVolume);
                if (num > 0) {
                    FluidsManager.GetFluid(tankValue).Emit(tank.Project, position, velocity);
                }
            }
        }

        public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap) {
            int value = valuesDictionary.GetValue<int>("TanksCount");
            for (int i = 0; i < value; i++) {
                m_tanks.Add(new Tank());
            }
            ValuesDictionary value2 = valuesDictionary.GetValue<ValuesDictionary>("Tanks");
            for (int j = 0; j < m_tanks.Count; j++) {
                ValuesDictionary value3 = value2.GetValue<ValuesDictionary>("Tank" + j.ToString(CultureInfo.InvariantCulture), null);
                if (value3 != null) {
                    Tank tank = m_tanks[j];
                    tank.Value = value3.GetValue<int>("Contents");
                    tank.Volume = value3.GetValue<float>("Volume");
                }
            }
        }

        public override void Save(ValuesDictionary valuesDictionary, EntityToIdMap entityToIdMap) {
            var valuesDictionary2 = new ValuesDictionary();
            valuesDictionary.SetValue("Tanks", valuesDictionary2);
            for (int i = 0; i < m_tanks.Count; i++) {
                Tank tank = m_tanks[i];
                if (tank.Volume > 0) {
                    var valuesDictionary3 = new ValuesDictionary();
                    valuesDictionary2.SetValue("Tank" + i.ToString(CultureInfo.InvariantCulture), valuesDictionary3);
                    valuesDictionary3.SetValue("Contents", tank.Value);
                    valuesDictionary3.SetValue("Volume", tank.Volume);
                }
            }
        }

        /// <summary>
        /// 获取指定槽的流体ID
        /// </summary>
        /// <param name="tankIndex"></param>
        /// <returns></returns>
        public virtual int GetTankValue(int tankIndex) {
            if (tankIndex >= 0
                && tankIndex < m_tanks.Count) {
                if (m_tanks[tankIndex].Volume <= 0) {
                    return 0;
                }
                return m_tanks[tankIndex].Value;
            }
            return 0;
        }

        /// <summary>
        /// 获取指定槽的流体体积
        /// </summary>
        /// <param name="tankIndex"></param>
        /// <returns></returns>
        public virtual float GetTankVolume(int tankIndex) {
            if (tankIndex >= 0
                && tankIndex < m_tanks.Count) {
                return m_tanks[tankIndex].Volume;
            }
            return 0;
        }

        /// <summary>
        /// 获取指定槽对指定流体的容积
        /// (默认直接获取指定槽的最大容积)
        /// </summary>
        /// <param name="tankIndex"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual float GetTankCapacity(int tankIndex, int value) => GetTankOriginalCapacity(tankIndex);

        public virtual float GetTankOriginalCapacity(int tankIndex) {
            if (tankIndex >= 0
                && tankIndex < m_tanks.Count) {
                return m_tanks[tankIndex].Capacity;
            }
            return 0;
        }

        /// <summary>
        /// 获取某容器内的水是否可被舀起
        /// </summary>
        /// <param name="tankIndex"></param>
        /// <param name="container"></param>
        /// <returns></returns>
        public virtual bool GetTankScoopable(int tankIndex, int container) {
            int tankValue = GetTankValue(tankIndex);
            Fluid fluid = FluidsManager.GetFluid(tankValue);
            if (fluid is IScoopableFluid scoopableFluid) {
                return scoopableFluid.IsScoopable(container, out _);
            }
            return false;
        }

        /// <summary>
        /// 向某个槽存入流体
        /// </summary>
        /// <param name="tankIndex"></param>
        /// <param name="value"></param>
        /// <param name="volume"></param>
        /// <param name="overflow">溢出的流体体积</param>
        /// <exception cref="InvalidOperationException"></exception>
        public virtual void AddTankFluid(int tankIndex, int value, float volume, out float overflow) {
            overflow = 0;
            if (volume > 0
                && tankIndex >= 0
                && tankIndex < m_tanks.Count) {
                Tank tank = m_tanks[tankIndex];
                if ((GetTankVolume(tankIndex) != 0 && GetTankValue(tankIndex) != value)) {
                    throw new InvalidOperationException("Cannot add slot items.");
                }
                overflow = GetTankVolume(tankIndex) + volume - GetTankCapacity(tankIndex, value);
                tank.Value = value;
                tank.Volume = overflow > 0 ? tank.Capacity : tank.Volume + volume;
            }
        }

        /// <summary>
        /// 移除某个容器内的指定体积的液体
        /// </summary>
        /// <param name="tankIndex"></param>
        /// <param name="volume"></param>
        /// <returns>实际移除了多少液体</returns>
        public virtual float RemoveTankFluid(int tankIndex, float volume) {
            if (tankIndex >= 0
                && tankIndex < m_tanks.Count) {
                Tank tank = m_tanks[tankIndex];
                volume = MathUtils.Min(volume, GetTankVolume(tankIndex));
                tank.Volume -= volume;
                return volume;
            }
            return 0;
        }

        /// <summary>
        /// 清空所有槽内的流体
        /// </summary>
        /// <param name="position"></param>
        public virtual void EmitAllFluids(Vector3 position) {
            for (int i = 0; i < TanksCount; i++) {
                EmitTankFluid(this, i, position, m_random.Float(5f, 10f) * Vector3.Normalize(new Vector3(m_random.Float(-1f, 1f), m_random.Float(1f, 2f), m_random.Float(-1f, 1f))));
            }
        }
    }
}